package eg.backend.util;

import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;

import org.apache.commons.lang.time.DateUtils;

import eg.backend.query.DateRange;
import eg.backend.query.DateRange.DateId;

public class DateCalculator {
	public static Date getBegin(Date date, TimeDimension timeDimension) {
		if (timeDimension == null || date == null)
			return date;
		int isInYear = isInYear(date, timeDimension.getYear());
		if (isInYear > 0) {
			return date;
		}
		if (isInYear < 0) {
			return getFirstDay(timeDimension.getYear(), timeDimension.getMonth() > 0 ? timeDimension.getMonth() : 0, timeDimension.getWeek() > 0 ? timeDimension.getWeek() : -1);
		}

		int isInMonth = isInMonth(date, timeDimension.getYear(), timeDimension
				.getMonth());
		if (isInMonth > 0) {
			return date;
		}
		if (isInMonth < 0) {
			return getFirstDay(timeDimension.getYear(), timeDimension
					.getMonth(), timeDimension.getWeek() > 0 ? timeDimension.getWeek() : -1);
		}

		int isInWeek = isInWeek(date, timeDimension.getYear(), timeDimension
				.getMonth(), timeDimension.getWeek());
		if (isInWeek > 0) {
			return date;
		}
		if (isInWeek < 0) {
			return getFirstDay(timeDimension.getYear(), timeDimension
					.getMonth(), timeDimension.getWeek());
		}

		return date;
	}

	public static Date getEnd(Date date, TimeDimension timeDimension) {
		if (timeDimension == null || date == null)
			return date;
		int isInYear = isInYear(date, timeDimension.getYear());
		if (isInYear < 0) {
			return date;
		}
		if (isInYear > 0) {
			return getLastDay(timeDimension.getYear(), timeDimension.getMonth() > 0 ? timeDimension.getMonth() : Calendar.DECEMBER, timeDimension.getWeek() > 0 ? timeDimension.getWeek() : -1);
		}

		int isInMonth = isInMonth(date, timeDimension.getYear(), timeDimension
				.getMonth());
		if (isInMonth < 0) {
			return date;
		}
		if (isInMonth > 0) {
			return getLastDay(timeDimension.getYear(),
					timeDimension.getMonth(), timeDimension.getWeek() > 0 ? timeDimension.getWeek() : -1);
		}

		int isInWeek = isInWeek(date, timeDimension.getYear(), timeDimension
				.getMonth(), timeDimension.getWeek());
		if (isInWeek < 0) {
			return date;
		}
		if (isInWeek > 0) {
			return getLastDay(timeDimension.getYear(),
					timeDimension.getMonth(), timeDimension.getWeek());
		}

		return date;
	}

	@SuppressWarnings("deprecation")
	public static DateRange[] getRanges(Date from, Date to, TimeDimension timeDimension) {
		Calendar _from = Calendar.getInstance(), _to = Calendar.getInstance();
		_from.setTime(from);
		_to.setTime(to);

		List<DateRange> list = new ArrayList<DateRange>();
		if (timeDimension == null || timeDimension.getYear() < 0) {
			int fromYear = _from.get(Calendar.YEAR);
			int toYear = _to.get(Calendar.YEAR);
			if (toYear == fromYear) {
				list.add(new DateRange(from, to, "" + fromYear, new DateId(fromYear)));
			} else {
				list.add(new DateRange(from, getFirstDay(fromYear + 1,
						Calendar.JANUARY, -1), "" + fromYear, new DateId(fromYear)));
				for (int y = fromYear + 1; y < toYear; y++) {
					list.add(new DateRange(
							getFirstDay(y, Calendar.JANUARY, -1), getFirstDay(
									y + 1, Calendar.DECEMBER, -1), "" + y, new DateId(y)));
				}
				list.add(new DateRange(getFirstDay(toYear, Calendar.JANUARY, -1), to, "" + toYear, new DateId(toYear)));
			}
		} else {
			int year = timeDimension.getYear(), month = timeDimension
					.getMonth(), week = timeDimension.getWeek();
			if (month < 0) {
				int fromMonth = _from.get(Calendar.MONTH);
				int toMonth = _to.get(Calendar.MONTH);
				if (toMonth == fromMonth) {
					list.add(new DateRange(from, to, month(fromMonth+1), new DateId(year, fromMonth)));
				} else {
					list.add(new DateRange(from, getFirstDay(year, fromMonth + 1, -1), month(fromMonth+1), new DateId(year, fromMonth)));
					for (int y = fromMonth + 1; y < toMonth; y++) {
						list.add(new DateRange(getFirstDay(year, y, -1), getFirstDay(year, y + 1, -1), month(y+1), new DateId(year, y)));
					}
					list.add(new DateRange(getFirstDay(year, toMonth, -1), to, month(toMonth+1), new DateId(year, toMonth)));
				}
			} else if (week < 0) {
				int fromWeek = _from.get(Calendar.WEEK_OF_MONTH);
				int toWeek = _to.get(Calendar.WEEK_OF_MONTH);
				if (toWeek == fromWeek) {
					list.add(new DateRange(from, to, week(fromWeek), new DateId(year, month, fromWeek)));
				} else {
					list.add(new DateRange(from, getFirstDay(year, month,
							fromWeek + 1), week(fromWeek), new DateId(year, month, fromWeek)));
					for (int y = fromWeek + 1; y < toWeek; y++) {
						list.add(new DateRange(getFirstDay(year, month, y),
								getFirstDay(year, month, y + 1), week(y), new DateId(year, month, y)));
					}
					list.add(new DateRange(getFirstDay(year, month, toWeek), to, week(toWeek), new DateId(year, month, toWeek)));
				}
			} else {
				int fromDay = _from.get(Calendar.DAY_OF_WEEK);
				int toDay = _to.get(Calendar.DAY_OF_WEEK);
				if (toDay > fromDay) {
					Date today = from;
					Date tomorrow = getTomorrow(from);
					for (int y = fromDay; y <= toDay; y++) {
						list.add(new DateRange(today, tomorrow, day(today.getDate()), new DateId(year, month, week, -1)));
						today = tomorrow;
						tomorrow = getTomorrow(today);
					}
				}
			}
		}
		return list.toArray(new DateRange[] {});
	}

	public static Date getTomorrow(Date today) {
		today = DateUtils.truncate(today, Calendar.DATE);
		return DateUtils.addDays(today, 1);
	}
	
	private static Date getFirstDay(int year, int month, int week) {
		Calendar c = Calendar.getInstance();
		c.set(Calendar.YEAR, year);
		if(month < 0)
			month = Calendar.JANUARY;
		c.set(Calendar.MONTH, month);
		if (week < 0) {
			c.set(Calendar.DAY_OF_MONTH, 1);
		} else {
			c.set(Calendar.WEEK_OF_MONTH, week);
			c.set(Calendar.DAY_OF_WEEK, c.getActualMinimum(Calendar.DAY_OF_WEEK));
		}
		return DateUtils.truncate(c, Calendar.DATE).getTime();
	}

	private static Date getLastDay(int year, int month, int week) {
		Calendar c = Calendar.getInstance();
		c.set(Calendar.YEAR, year);
		if(month < 0)
			month = Calendar.DECEMBER;
		c.set(Calendar.MONTH, month);
		if (week < 0) {
			c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
		} else {
			c.set(Calendar.WEEK_OF_MONTH, week);
			c.set(Calendar.DAY_OF_WEEK, c.getActualMaximum(Calendar.DAY_OF_WEEK));
		}
		return DateUtils.truncate(c, Calendar.DATE).getTime();
	}

	public static int isInYear(Date date, int year) {
		if (year < 0)
			return 0;
		Calendar c = Calendar.getInstance();
		c.setTime(date);
		int yearOfDate = c.get(Calendar.YEAR);
		return yearOfDate < year ? -1 : yearOfDate == year ? 0 : 1;
	}

	public static int isInMonth(Date date, int year, int month) {
		if (month < 0)
			return 0;
		int isInYear = isInYear(date, year);
		if (isInYear != 0)
			return -100;
		Calendar c = Calendar.getInstance();
		c.setTime(date);
		int monthOfDate = c.get(Calendar.MONTH);
		return monthOfDate < month ? -1 : monthOfDate == month ? 0 : 1;
	}

	public static int isInWeek(Date date, int year, int month, int week) {
		if (week < 0)
			return 0;
		int isInMonth = isInMonth(date, year, month);
		if (isInMonth != 0)
			return -100;
		Calendar c = Calendar.getInstance();
		c.setTime(date);
		int weekOfDate = c.get(Calendar.WEEK_OF_MONTH);
		return weekOfDate < week ? -1 : weekOfDate == week ? 0 : 1;
	}
	
	private static String month(int month) {
		return month + "月";
	}
	private static String week(int week) {
		return "第" + week + "周";
	}
	private static String day(int day) {
		return day + "号";
	}
}
